/*
 * NetworkClient.cpp
 *
 *  Created on: Apr 12, 2011
 *      Author: Alan Mattila
 */

#include <iostream>
#include <cassert>

#include "NetworkClient.h"
#include "../misc/debug.h"

using std::cout;
using std::endl;



NetworkClient::NetworkClient()
{
	board = new Board();
	tcpSocket = new QTcpSocket(this);
	if(!tcpSocket)
	{
		TRACE("");
		cout << "Error: unable to create new QTcpSocket\n";
	}

	blockSize = 0;

	//TRACE("Connecting tcpSocket readyRead() signal\n");
	//connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(GetPacketFromServer()));
}



NetworkClient::NetworkClient(string _hostname) : Network(_hostname)
{
	board = new Board();
	tcpSocket = new QTcpSocket(this);
	if(!tcpSocket)
	{
		TRACE("");
		cout << "Error: unable to create new QTcpSocket\n";
	}

	blockSize = 0;

	//TRACE("Calling tcpSocket readyRead() signal\n");
	//connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(GetPacketFromServer()));
}


/*********************************************************
 * NetworkClient::~NetworkClient()
 * Destructor function.
 ********************************************************/
NetworkClient::~NetworkClient()
{
	TRACE("Start: ~NetworkClient()\n");

	// Delete QTcpSocket
	if(tcpSocket)
		delete tcpSocket;

	TRACE("End: ~NetworkClient()\n");
}


/*********************************************************
 * int NetworkClient::ConnectToServer()
 * Initiates a connection to the server. Note that the
 * hostname must be set before this can be called.
 ********************************************************/
int NetworkClient::ConnectToServer()
{
	TRACE("Connecting to host: %s\n", hostname.c_str());
	// Create socket
	if(tcpSocket == NULL)
		tcpSocket = new QTcpSocket(this);
	if(!tcpSocket)
	{
		TRACE("");
		cout << "Error: unable to create new QTcpSocket\n";
		return NETWORK_ERROR;
	}

	// Connect slots
	connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(ReadyRead()));
	connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(Error()));
	connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(Disconnected()));

	// Connect to the server
	tcpSocket->connectToHost(QString(hostname.c_str()), (quint16) port);
	if(!tcpSocket->waitForConnected(-1))
	{
		TRACE("");
		cout << "Error: unable to connect to host at QTcpSocket::connectToHost()\n";
		cout << "  error " << tcpSocket->error();
		cout << ": " << tcpSocket->errorString().toStdString() << endl;
		cout << "  tcpScoket->state() = " << tcpSocket->state() << endl;
		return NETWORK_ERROR;
	}

	//if(GetPacketFromServer() == NETWORK_ERROR)
	//	return NETWORK_ERROR;

	//ParsePacket();

	TRACE("Successfully connected to server!\n");
	return 0;
}


/*********************************************************
 * int NetworkClient::UpdateBoard()
 * Causes the client to listen and wait for the board to
 * be updated. For example, when the hosting player makes
 * a move.
 ********************************************************/
int NetworkClient::UpdateBoard()
{
	if(GetPacketFromServer() == NETWORK_ERROR)
		return NETWORK_ERROR;

	if(ParsePacket() == NETWORK_ILLEGAL_MOVE)
		return NETWORK_ILLEGAL_MOVE;

	return 0;
}


/*********************************************************
 * int NetworkClient::MakeMove()
 * Call this to make a move. This sends the move
 * coordinates to the server. The server responds with an
 * updated game board or a message saying the move is not
 * legal.
 ********************************************************/
int NetworkClient::MakeMove(int x1, int y1, int x2, int y2)
{
	// Create a new packet
	assert(packet == NULL);
	packet = new Packet();

	// Create a new data stream
	QByteArray block;
	QDataStream out(&block, QIODevice::WriteOnly);
	out.setVersion( QDataStream::Qt_4_7 );

	// Assemble the packet
	packet->SetPacketType(Move);
	packet->SetPacketMove(x1, y1, x2, y2);

	// Copy packet data into QByteArray
	int packetSize = packet->GetPacketSize();
	out << (uint8_t)packetSize;
	for(int i=0; i<packetSize; i++)
	{
		out << packet->GetByte(i);
	}

	// TEST: display the contents of the packet
	packet->DisplayContents();

	// Delete the packet
	if(packet)
		delete packet;
	packet = NULL;

	// Write the data to the socket
	TRACE("Writing block of data to tcpSocket\n");
	qint64 numBytes = tcpSocket->write(block);
	TRACE("%d bytes written\n", (int) numBytes);

	return 0;
}


/*********************************************************
 * void NetworkClient::GetPacketFromServer()
 ********************************************************/
int NetworkClient::GetPacketFromServer()
{
	TRACE("Start: GetPacketFromServer()\n");
	assert(tcpSocket != NULL);
	TRACE("tcpSocket->bytesAvailable() = %d\n", tcpSocket->bytesAvailable());

	QDataStream in(tcpSocket);
	in.setVersion(QDataStream::Qt_4_7);
	if(blockSize == 0)
	{
		while(tcpSocket->bytesAvailable() < (int)sizeof(quint8)) { }
		in >> blockSize;
		TRACE("Read blockSize = %d\n", blockSize);
	}

	while(tcpSocket->bytesAvailable() < blockSize) { }
	TRACE("Reading in all data\n");
	QByteArray data = tcpSocket->readAll();
	/*data.reserve(blockSize);
	if(in.status() == QDataStream::Ok)
		TRACE("in.status() = QDataStream::Ok\n");
	else if(in.status() == QDataStream::ReadPastEnd)
		TRACE("in.status() = QDataStream::ReadPastEnd\n");
	else if(in.status() == QDataStream::ReadCorruptData)
		TRACE("in.status() = QDataStream::ReadCorruptData\n");
	in >> data;*/
	TRACE("data.size() = %d\n", data.size());
	/*if(in.status() == QDataStream::Ok)
		TRACE("in.status() = QDataStream::Ok\n");
	else if(in.status() == QDataStream::ReadPastEnd)
		TRACE("in.status() = QDataStream::ReadPastEnd\n");
	else if(in.status() == QDataStream::ReadCorruptData)
		TRACE("in.status() = QDataStream::ReadCorruptData\n");*/

	// Create new packet
	assert(packet == NULL);
	packet = new Packet();

	// Copy data into Packet class
	for(int i=0; i<(int)blockSize; i++)
	{
		//TRACE("data[%2d]: %d\n", i, data[i]);
		packet->SetByte(data[i], i);
	}

	blockSize = 0;

	// Now parse the packet
	return ParsePacket();
}


/*********************************************************
 * void NetworkClient::ParsePacket()
 ********************************************************/
int NetworkClient::ParsePacket()
{
	TRACE("Start: ParsePacket()\n");
	assert(packet != NULL);
	packet->DisplayContents();
	if(packet->GetPacketType() == Message)
	{
		TRACE("Packet Type: Message\n");
		if(packet->GetPacketMessage() == IllegalMove)
		{
			TRACE("Packet Message: IllegalMove\n");
			TRACE("Emmiting signal: messageReceived()\n");
			emit messageReceived(IllegalMove);
		}
	}
	else if(packet->GetPacketType() == GameData)
	{
		TRACE("Packet Type: GameData\n");
		// Set game status
		status = packet->GetPacketStatus();

		// Set game board
		packet->GetPacketGameBoard(board);

		// Test: output the new board
		//TRACE("Outputting the new board\n");
		//std::cout << *board;

		TRACE("Emitting signal: gameBoardReceived()\n");
		emit gameBoardReceived();
	}
	else
	{
		TRACE("Error: unknown packet type!\n");
	}

	/*TRACE("Emmiting signal: packetReceived()\n");
	emit packetReceived(packet->GetPacketType());*/

	// Delete the packet
	if(packet)
		delete packet;
	packet = NULL;

	TRACE("End: ParsePacket()\n");

	return 0;
}


void NetworkClient::ReadyRead(void)
{
	TRACE("");
	qDebug() << "readyRead() signal received";
	this->GetPacketFromServer();
}


void NetworkClient::Error(void)
{
	TRACE("");
	qDebug() << "error() signal received!";
	qDebug() << "  " << tcpSocket->errorString();
}


void NetworkClient::Disconnected(void)
{
	TRACE("");
	qDebug() << "disconnected() signal received";
}

